--
-- Author: ping.wang
-- Date: 2018-07-31 16:19:51
-- 循环滚动的控件
-- ScrollCircleView
--

local fileName = string.getModeName(...)
local ClassRef = class(fileName)

local MOVE_SPEED = 250 --标量速度 不具有方向 1秒移动像素速度

-- params
-- parentNode  父节点
-- size 剪裁区域
-- isVertical 是否是垂直显示 这一条可以不写
-- subNodeClass 子节点类
-- paddingLen 子节点之间的间距，循环滚动控件只支持等间距,第一个节点从(0 + nodeWidth,size.height/2)或者(size.width/2,size.height - nodeHeight)的地方开始
-- selectedCb 选中回调
-- subNodeScales 显示子节点的缩放比 若不写 默认都是1 若写了 则类似 {min=0.5,max=1}
-- selectedMode 0 为left or top 1为center 2为right or bottom
function ClassRef:ctor(params)
	self._contentSize = params.size
	self._parentNode = params.parentNode
	self._subNodeClass = params.subNodeClass
	self._isVertical = params.isVertical or false
	self._paddingLen = params.paddingLen or 0
	self._selectedMode = params.selectedMode or 1
	self._subNodeScales = params.subNodeScales
	self._selectedCb = params.selectedCb

	local testSp = display.newScale9Sprite("public/public_black.png",0, 0, self._contentSize)
	testSp:setAnchorPoint(0,0)

	local clipNode = cc.ClippingNode:create()
	clipNode:setInverted(false)
	clipNode:setAlphaThreshold(1)
	clipNode:setStencil(testSp)
	clipNode:setName("di")
	clipNode:setAnchorPoint(0,0)
	clipNode:setPosition(0,0)
	clipNode:setContentSize(self._contentSize)
	self._parentNode:addChild(clipNode)

	local listener = cc.EventListenerTouchOneByOne:create()
	listener:setSwallowTouches(false)
	listener:registerScriptHandler(handler(self,self.onTouchBegan),cc.Handler.EVENT_TOUCH_BEGAN)
	listener:registerScriptHandler(handler(self,self.onTouchEnded),cc.Handler.EVENT_TOUCH_ENDED)
	listener:registerScriptHandler(handler(self,self.onTouchMoved),cc.Handler.EVENT_TOUCH_MOVED)
	listener:registerScriptHandler(handler(self,self.onTouchCancelled),cc.Handler.EVENT_TOUCH_CANCELLED)
	local eventDispatcher = self._parentNode:getEventDispatcher()
	eventDispatcher:addEventListenerWithSceneGraphPriority(listener, clipNode)

	clipNode:scheduleUpdate( handler(self, self.updateFrame))

	--初始化显示多少个node
	self._clippingNode = clipNode
	self._subNodeList = {} --显示的node需要放这里
	self._minPos = 0
	self._maxPos = 0
	self._subLen = 0 --子节点的长度或者宽度 若是垂直 则用subPanelContentSize.height
	self._nodePaddingLen = 0 --子节点与子节点之间的距离
	self._beginMoveDis = 0
	self._curSelectedIdx = 0 --记录下原来选中的节点
	self:_initSubNodeList()
end

function ClassRef:_initSubNodeList()
	local clippingLen = self._contentSize.width
	if self._selectedMode == 0 then
		local tempNode = self._subNodeClass.create()
		local subPanelContentSize = tempNode:getContentSize() --子节点的宽高
		self._subLen = subPanelContentSize.width --子节点的长度或者宽度 若是垂直 则用subPanelContentSize.height
		local startP = self._subLen/2 --开始的位置
		self._nodePaddingLen = self._subLen + self._paddingLen --子节点与子节点之间的距离
		self._minPos = startP - self._nodePaddingLen
		tempNode:setAnchorPoint(0.5,0.5)
		self:setNodePos(tempNode,self._minPos,self._contentSize.height/2)
		self._clippingNode:addChild(tempNode)
		table.insert(self._subNodeList,tempNode)
		--从1开始到裁剪区结束
		while(startP <= clippingLen)
		do
			tempNode = self._subNodeClass.create()
			tempNode:setAnchorPoint(0.5,0.5)
			self:setNodePos(tempNode,startP,self._contentSize.height/2)
			self._clippingNode:addChild(tempNode)
			startP = startP + self._nodePaddingLen
			table.insert(self._subNodeList,tempNode)
		end
		--右边裁剪区外
		tempNode = self._subNodeClass.create()
		self._maxPos = startP
		tempNode:setAnchorPoint(0.5,0.5)
		self:setNodePos(tempNode,self._maxPos,self._contentSize.height/2)
		self._clippingNode:addChild(tempNode)
		table.insert(self._subNodeList,tempNode)
	elseif self._selectedMode == 2 then
		local tempNode = self._subNodeClass.create()
		local subPanelContentSize = tempNode:getContentSize() --子节点的宽高
		self._subLen = subPanelContentSize.width --子节点的长度或者宽度 若是垂直 则用subPanelContentSize.height
		local startP = clippingLen- self._subLen/2 --开始的位置
		self._nodePaddingLen = self._subLen + self._paddingLen --子节点与子节点之间的距离
		self._maxPos = startP + self._nodePaddingLen
		tempNode:setAnchorPoint(0.5,0.5)
		self:setNodePos(tempNode,self._maxPos,self._contentSize.height/2)
		self._clippingNode:addChild(tempNode)
		table.insert(self._subNodeList,tempNode)
		--中间
		while(startP >= 0 )
		do
			tempNode = self._subNodeClass.create()
			tempNode:setAnchorPoint(0.5,0.5)
			self:setNodePos(tempNode,startP,self._contentSize.height/2)
			self._clippingNode:addChild(tempNode)
			startP = startP - self._nodePaddingLen
			table.insert(self._subNodeList,tempNode)
		end
		--最左边超出部分
		tempNode = self._subNodeClass.create()
		self._minPos = startP
		tempNode:setAnchorPoint(0.5,0.5)
		self:setNodePos(tempNode,startP,self._contentSize.height/2)
		self._clippingNode:addChild(tempNode)
		table.insert(self._subNodeList,tempNode)
	elseif self._selectedMode == 1 then
		local tempNode = self._subNodeClass.create()
		local subPanelContentSize = tempNode:getContentSize() --子节点的宽高
		self._subLen = subPanelContentSize.width --子节点的长度或者宽度 若是垂直 则用subPanelContentSize.height
		local startP = clippingLen/2--开始的位置
		self._nodePaddingLen = self._subLen + self._paddingLen --子节点与子节点之间的距离
		tempNode:setAnchorPoint(0.5,0.5)
		self:setNodePos(tempNode,startP,self._contentSize.height/2)
		self._clippingNode:addChild(tempNode)
		table.insert(self._subNodeList,tempNode)
		--左边
		startP = startP - self._nodePaddingLen
		while(startP >= 0)
		do
			tempNode = self._subNodeClass.create()
			tempNode:setAnchorPoint(0.5,0.5)
			self:setNodePos(tempNode,startP,self._contentSize.height/2)
			self._clippingNode:addChild(tempNode)
			startP = startP - self._nodePaddingLen
			table.insert(self._subNodeList,tempNode)
		end

		--最左边超出部分
		tempNode = self._subNodeClass.create()
		self._minPos = startP
		tempNode:setAnchorPoint(0.5,0.5)
		self:setNodePos(tempNode,startP,self._contentSize.height/2)
		self._clippingNode:addChild(tempNode)
		table.insert(self._subNodeList,tempNode)

		--右边
		startP = clippingLen/2 + self._nodePaddingLen
		while(startP <= clippingLen)
		do
			tempNode = self._subNodeClass.create()
			tempNode:setAnchorPoint(0.5,0.5)
			self:setNodePos(tempNode,startP,self._contentSize.height/2)
			self._clippingNode:addChild(tempNode)
			startP = startP + self._nodePaddingLen
			table.insert(self._subNodeList,tempNode)
		end
		--最右边超出部分
		tempNode = self._subNodeClass.create()
		self._maxPos = startP
		tempNode:setAnchorPoint(0.5,0.5)
		self:setNodePos(tempNode,startP,self._contentSize.height/2)
		self._clippingNode:addChild(tempNode)
		table.insert(self._subNodeList,tempNode)
	end

	self._minPos = self._minPos - self._nodePaddingLen
	self._maxPos = self._maxPos + self._nodePaddingLen

	--给所有的node添加一个点击事件 使得点击的时候滚动到相应的节点
	for k,v in ipairs(self._subNodeList) do
		v:setTouchEnabled(true)
		v:setSwallowTouches(false)
		display.uiAddClick(v, function()
			if self._canClickSubNode then
				local posX,posY = v:getPosition()
				local startP = 0
				if self._selectedMode == 0 then
					startP = self._subLen/2
				elseif self._selectedMode == 1 then
					startP = self._contentSize.width/2
				else
					startP = self._contentSize.width - self._subLen/2
				end

				if math.round(math.abs(posX - startP)) >= self._nodePaddingLen then
					if posX > startP then
						self:setSelectedIdxByMoveAct(self:_getIdxByPos(posX),-1)
					else
						self:setSelectedIdxByMoveAct(self:_getIdxByPos(posX),1)
					end
				end
				self._canClickSubNode = false
			end
		end)
	end

end

--data = {{},{},{},{}}
--初始化的时候调用 平时不可以调用 首次显示调用
function ClassRef:setData(data,defaultSelectIdx)
	self._data = data --显示用的数据
	self._beginMove = 0 --记录一下当前移动的距离 要么是x要么是y
	if not defaultSelectIdx then defaultSelectIdx = 1 end
	self:_setSelectedNodeByIdx(defaultSelectIdx)
end

--刷新的时候调用
function ClassRef:refreshData(data)
	self._data = data --显示用的数据
	for i = 1,#self._subNodeList do
		local idx = self._subNodeList[i]:getData().id
		self._subNodeList[i]:setData(self._data[idx])
	end
end

function ClassRef:_setSelectedNodeByIdx(idx)
	local startPos = 0
	if self._selectedMode == 0 then
		startPos = self._subLen/2
	elseif self._selectedMode == 1 then
		startPos = self._contentSize.width/2
	elseif self._selectedMode == 2 then
		startPos = self._contentSize.width - self._subLen/2
	end

	local num = 1
	local disStartPos = 0
	--local tempMin = 0
	--self._initStartIdx = 1 --初始化的时候最左边的idx
	for i = 1,#self._subNodeList do
		disStartPos = self._subNodeList[i]:getPositionX()- startPos

		num = math.floor( (disStartPos/self._nodePaddingLen + idx))%(#self._data) --因为是前闭后开原则 这样 -1.5 属于 -2     而  1.5属于1
		if num <= 0 then num = #self._data + num end
		self._subNodeList[i]:setData(self._data[num])

		--if tempMin > self._subNodeList[i]:getPositionX() then
		--tempMin = self._subNodeList[i]:getPositionX()
		--self._initStartIdx = num
		--end
	end
	self._initSelectedIdx = idx
	self._moveDistance = 0 --重置移动距离

	if self._selectedCb then
		self._selectedCb(self._data[idx])
	end
	self._curSelectedIdx = idx
end

function ClassRef:getSelectedData()
	local idx = self:getSelectedIdx()
	return self._data[idx]
end

function ClassRef:_updateNodePosByMoveDis(moveX,moveY)
	self._moveDistance = self._moveDistance + moveX
	local newPos = 0

	for i = 1,#self._subNodeList do
		local oldX,oldY = self._subNodeList[i]:getPosition()
		newPos = oldX + moveX
		if newPos < self._minPos then
			local deltaLen = self._minPos - newPos
			--比最小值小
			newPos = self._maxPos - self._nodePaddingLen - deltaLen
			--同时要设置当前点的数据
			local tempDeltaIdx = self:_getIdxByPos(newPos)
			self._subNodeList[i]:setData(self._data[tempDeltaIdx])
			self._subNodeList[i]:setSelected(self._curSelectedIdx == tempDeltaIdx)
		elseif newPos > self._maxPos then
			--比最大值大
			local deltaLen = newPos - self._maxPos
			newPos = self._minPos + self._nodePaddingLen + deltaLen
			local tempDeltaIdx = self:_getIdxByPos(newPos)
			--同时要设置当前点的数据
			--dump(tempDeltaIdx,deltaLen)
			self._subNodeList[i]:setData(self._data[tempDeltaIdx])
			self._subNodeList[i]:setSelected(self._curSelectedIdx == tempDeltaIdx)
		end
		self:setNodePos(self._subNodeList[i],newPos,oldY)
	end
end

--获得合法的下标索引
function ClassRef:_getLegalDataIdx(idx)
	idx = idx %(#self._data)
	if idx <= 0 then
		idx = #self._data + idx
	end
	return idx
end

--获得当前选中的节点
--我们有一个初始化选中节点
--还有一个当前已经移动的distance distance>= -#self._data * self._nodePaddingLen and distance <=   #self._data * self._nodePaddingLen
function ClassRef:getSelectedIdx()
	if self._moveDistance == 0 then
		return self._initSelectedIdx
	end

	local tempLen = 0
	local totalLen = #self._data * self._nodePaddingLen
	if self._moveDistance >= 0 then
		tempLen = -math.floor((self._moveDistance%totalLen)/self._nodePaddingLen)
	else
		tempLen = math.floor((-self._moveDistance%totalLen)/self._nodePaddingLen)
	end
	return self:_getLegalDataIdx(self._initSelectedIdx + tempLen)
end

function ClassRef:_refreshContent(pos)
	--第一步 根据显示的开始索引 重新设置子元素的
	if self._isVertical then
		--todo
	else
		self:_updateNodePosByMoveDis(pos.x,pos.y)
	end
end

function ClassRef:onTouchBegan(touch,event)
	--若没有数据 不用响应点击事件
	if not self._data then return false end

	local locationX,locationY = touch:getLocation2()
	local size = self._parentNode:getContentSize()
	local pos = self._parentNode:convertToNodeSpace(cc.p(locationX,locationY))
	if pos.x >= 0 and pos.y >= 0 and pos.x <= size.width and pos.y <= size.height then
		self:_endMove()
		self._canClickSubNode = true
		self._startMoved = false
		self._beginTouchPoint = cc.p(locationX,locationY)
		return true
	end

	return false
end

function ClassRef:onTouchMoved(touch,event)
	local locationX,locationY = touch:getLocation2()
	local delta = touch:getDelta()
	self:_refreshContent(delta)
	--5像素内的移动不算移动 算点击
	if self._canClickSubNode and math.abs(self._beginTouchPoint.x - locationX) > 5 then
		self._canClickSubNode = false
		self._startMoved = true
	end
end

function ClassRef:onTouchCancelled(touch,event)

end

function ClassRef:onTouchEnded(touch,event)
	--结束移动的时候调用调整节点位置的函数
	if self._startMoved then
		self:_adjustNodePos()
	end
end

--根据当前的移动总距离调整节点位置
function ClassRef:_adjustNodePos()
	local len = math.abs(self._moveDistance)% self._nodePaddingLen
	if len >= self._nodePaddingLen* 0.5 then
		len = self._nodePaddingLen - len
		if self._moveDistance < 0 then
			len = -len
		end
	else
		if self._moveDistance >= 0 then
			len = -len
		end
	end

	self:_startMove(len)
end

--设置节点的位置 同时要设置其缩放 以及 zorder

function ClassRef:setNodePos(node,posX,posY)

	node:setPosition(posX,posY)
	if not self._subNodeScales then return end

	if self._isVertical then

	else
		local referPos = self._subLen/2
		if self._selectedMode == 1 then
			referPos = self._contentSize.width/2
		elseif self._selectedMode == 2 then
			referPos = self._contentSize.width - self._subLen/2
		end

		local delta = math.abs(posX - referPos)
		if self._selectedMode == 1 then
			delta = 2 * delta /(self._contentSize.width - self._subLen/2)
		else
			delta = delta /self._contentSize.width
		end

		if delta >= 1 then delta = 1 end
		node:setScale(self._subNodeScales.max - delta * (self._subNodeScales.max - self._subNodeScales.min))
		node:setLocalZOrder(100 - delta * 100)
	end
end

--向哪个方向移动到指定点 dir = true 为正方向移动
function ClassRef:setSelectedIdx(idx,dir)
	idx = self:_getLegalDataIdx(idx)
	local curSelectedIdx = self:getSelectedIdx()
	local deltaIdx = idx - curSelectedIdx
	if dir > 0 then
		if deltaIdx >= 0 then
			deltaIdx = #self._data - deltaIdx
		end
	else
		if deltaIdx < 0 then
			deltaIdx = deltaIdx + #self._data
		end
	end

	self._canMove = false
	self._beginMoveDis = 0
	self._targetMoveDis = nil

	--这里需要计算一个差值 因为移动被中断 可能现在移动值离选中点有一定的误差
	local deltaMove = math.abs(self._moveDistance) % self._nodePaddingLen
	if self._moveDistance >= 0 then
		deltaMove = -deltaMove
	end

	local targetMoveDistance = deltaIdx * self._nodePaddingLen * dir + deltaMove
	self:_updateNodePosByMoveDis(targetMoveDistance,0)

	self._curSelectedIdx = self:getSelectedIdx()

	if self._selectedCb then
		self._selectedCb(self._data[self._curSelectedIdx])
	end
end

--设置选中的索引 并且通过移动的方式过去dir = 1 为正方向移动
function ClassRef:setSelectedIdxByMoveAct(idx,dir)
	idx = self:_getLegalDataIdx(idx)
	local curSelectedIdx = self:getSelectedIdx()

	local deltaIdx = idx - curSelectedIdx

	if dir>0 then
		if deltaIdx >= 0 then
			deltaIdx = #self._data - deltaIdx
		else
			deltaIdx = -deltaIdx
		end

	else
		if deltaIdx < 0 then
			deltaIdx = deltaIdx + #self._data
		end
	end

	self._canMove = false
	self._beginMoveDis = 0
	self._targetMoveDis = nil

	--这里需要计算一个差值 因为移动被中断 可能现在移动值离选中点有一定的误差
	local deltaMove = math.abs(self._moveDistance) % self._nodePaddingLen
	if self._moveDistance >= 0 then
		deltaMove = -deltaMove
	end

	local targetMoveDistance = deltaIdx * self._nodePaddingLen * dir + deltaMove

	self:_startMove(targetMoveDistance)
end

function ClassRef:_startMove(moveDis)
	self._targetMoveDis = math.abs(moveDis)
	self._moveDirection = 1
	if moveDis < 0 then self._moveDirection = -1 end
	--在指定时间内移动moveDis长度
	self._canMove = true
	self._beginMoveDis = 0
end

function ClassRef:_endMove()
	self._canMove = false
	self._targetMoveDis = nil
	--结束的时候我们重新计算下moveDistance
	local totalLen = #self._data * self._nodePaddingLen
	totalLen = math.abs(self._moveDistance)%totalLen
	if self._moveDistance < 0 then
		self._moveDistance = -totalLen
	else
		self._moveDistance = totalLen
	end

	self._curSelectedIdx = self:getSelectedIdx()

	if self._beginMoveDis > 0 then
		if self._selectedCb then
			self._selectedCb(self._data[self._curSelectedIdx])
		end
	end
	self._beginMoveDis = 0
end

function ClassRef:updateFrame(dt)
	if not self._canMove or not self._targetMoveDis then return end
	local s = dt * MOVE_SPEED --当前帧移动距离

	if self._beginMoveDis + s > self._targetMoveDis then
		s = self._targetMoveDis - self._beginMoveDis
		self._beginMoveDis = self._targetMoveDis
	else
		self._beginMoveDis = self._beginMoveDis + s
	end

	self:_updateNodePosByMoveDis(s * self._moveDirection,0)
	if self._beginMoveDis >= self._targetMoveDis then
		self:_endMove()
	end
end

function ClassRef:goNextSelect(hasMoveAct)
	local curSelectIdx = self:getSelectedIdx()
	if hasMoveAct then
		self:setSelectedIdxByMoveAct(curSelectIdx + 1,-1)
	else
		self:setSelectedIdx(curSelectIdx + 1,-1)
	end
end

function ClassRef:goPreSelect(hasMoveAct)
	local curSelectIdx = self:getSelectedIdx()
	if hasMoveAct then
		self:setSelectedIdxByMoveAct(curSelectIdx - 1,1)
	else
		self:setSelectedIdx(curSelectIdx - 1,1)
	end
end

function ClassRef:_getIdxByPos(pos)
	local curSelectedIdx = self:getSelectedIdx()
	local startP = 0
	if self._selectedMode == 0 then
		startP = self._subLen/2
	elseif self._selectedMode == 1 then
		startP = self._contentSize.width/2
	else
		startP = self._contentSize.width - self._subLen/2
	end

	local tempLen = math.abs(self._moveDistance) % self._nodePaddingLen
	if self._moveDistance < 0 then
		tempLen = -tempLen
	end
	startP = startP + tempLen

	local deltaLen = pos - startP
	local deltaIdx = math.round(math.abs(deltaLen)/self._nodePaddingLen)

	local targetIdx = 0
	if deltaLen >= 0 then
		targetIdx = curSelectedIdx + deltaIdx
	else
		targetIdx = curSelectedIdx - deltaIdx
	end

	return self:_getLegalDataIdx(targetIdx)
end

--返回现在的数据
function ClassRef:getData()
	return self._data
end

function ClassRef:getAllNodes()
	return self._subNodeList
end

return ClassRef